package com.github.iogogogo.common.util;

import com.github.iogogogo.common.util.ts.TimestampParser;
import com.github.iogogogo.common.util.ts.TimestampParserFactory;
import com.github.iogogogo.common.util.ts.parser.UnixEpochParser;
import com.github.iogogogo.common.util.ts.parser.UnixMillisEpochParser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created by tao.zeng on 2021/3/3.
 */
@Slf4j
public class Java8DateTimeUtils {

    /**
     * Now date time local date time.
     *
     * @return the local date time
     */
    public static LocalDateTime nowDateTime() {
        return LocalDateTime.now();
    }

    /**
     * Now date local date.
     *
     * @return the local date
     */
    public static LocalDate nowDate() {
        return LocalDate.now();
    }

    /**
     * Try parser local date time.
     *
     * @param value   the value
     * @param locale  the locale
     * @param pattern the pattern
     * @return the local date time
     */
    public static LocalDateTime tryParse(String value, Locale locale, List<String> pattern) {
        TimestampParser timestampParser = TimestampParserFactory.makeParser(value);

        if (timestampParser instanceof UnixEpochParser || timestampParser instanceof UnixMillisEpochParser) {
            return timestampParser.parse(null);
        } else {
            boolean flag;
            List<String> patterns = pattern.stream().filter(StringUtils::isNotEmpty).collect(Collectors.toList());
            List<String> collect = (flag = !CollectionUtils.isEmpty(patterns)) ? patterns : TimestampParser.PATTERN_LIST;

            List<DateTimeFormatter> timeFormatters = collect.stream()
                    .peek(x -> {
                        if (log.isDebugEnabled())
                            log.debug("init {} parser ofPattern: {}", flag ? "customer" : "system", x);
                    }).map(x -> DateTimeFormatter.ofPattern(x, locale)).collect(Collectors.toList());

            return timestampParser.parse(timeFormatters);
        }
    }

    /**
     * Try parser local date time.
     *
     * @param value   the value
     * @param locale  the locale
     * @param pattern the pattern
     * @return the local date time
     */
    public static LocalDateTime tryParse(String value, Locale locale, String... pattern) {
        return tryParse(value, locale, Stream.of(pattern).collect(Collectors.toList()));
    }

    /**
     * Try parser local date time.
     *
     * @param value   the value
     * @param pattern the pattern
     * @return the local date time
     */
    public static LocalDateTime tryParse(String value, List<String> pattern) {
        return tryParse(value, Locale.CHINA, pattern);
    }

    /**
     * Try parser local date time.
     *
     * @param value   the value
     * @param pattern the pattern
     * @return the local date time
     */
    public static LocalDateTime tryParse(String value, String... pattern) {
        return tryParse(value, Stream.of(pattern).collect(Collectors.toList()));
    }


    /**
     * To instant instant.
     *
     * @param localDateTime the local date time
     * @return the instant
     */
    public static Instant toInstant(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        return localDateTime.atZone(zone).toInstant();
    }

    /**
     * To epoch milli long.
     *
     * @param localDateTime the local date time
     * @return the long
     */
    public static long toEpochMilli(LocalDateTime localDateTime) {
        return toInstant(localDateTime).toEpochMilli();
    }

    /**
     * To epoch second long.
     *
     * @param localDateTime the local date time
     * @return the long
     */
    public static long toEpochSecond(LocalDateTime localDateTime) {
        return toInstant(localDateTime).toEpochMilli() / 1000;
    }

    /**
     * Of epoch milli instant.
     *
     * @param millis the millis
     * @return the instant
     */
    public static Instant ofEpochMilli(long millis) {
        return Instant.ofEpochMilli(millis);
    }

    /**
     * Of epoch second instant.
     *
     * @param second the second
     * @return the instant
     */
    public static Instant ofEpochSecond(long second) {
        return Instant.ofEpochSecond(second);
    }

    /**
     * To java 8 local date time.
     *
     * @param timestamp the timestamp
     * @return the local date time
     */
    public static LocalDateTime toJava8Milli(long timestamp) {
        return toJava8(ofEpochMilli(timestamp));
    }

    /**
     * To java 8 second local date time.
     *
     * @param timestamp the timestamp
     * @return the local date time
     */
    public static LocalDateTime toJava8Second(long timestamp) {
        return toJava8(ofEpochSecond(timestamp * 1000));
    }

    /**
     * To java 8 local date time.
     *
     * @param instant the instant
     * @return the local date time
     */
    public static LocalDateTime toJava8(Instant instant) {
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }
}
